package com.pnemec.util;

import java.awt.Component;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;

import javax.swing.*;



import com.pnemec.addlog.LogModule;

import sun.rmi.runtime.Log;

/**
 * This class will provide methods and store data for repetitive duties e.g.
 * store all images here and provide them. So only one instance of image is in
 * memory.
 * 
 * If you are using getImage, do not forget to call setTracker so all images are
 * ready at first use !!
 * 
 * this class is supposes to be use "staticaly". Actual class is created only once by
 * calling init() for first time. Acces to data, calling nonstatic method is done using
 * init().doSomething()
 * 
 * 
 * @author Pavel Nemec <nemecp4@gmail.com>
 * 
 */
public class DataProvider {
	/* we are reading images into buffer, this is max size */
	public static int MAX_IMAGE_SIZE = 200000;
	
	public static int DEFAULT_RESULT_TYPE=LogModule.TYPE_ALL_ZIPPED_UPLOAD;

	private static String imagesPath = "data/images";
	
	/* standard image for information buttons "I" */
	public static String INFO_IMAGE="/info.png";

	private static DataProvider self = null;

	/* list(cache) of picture we already load */
	private Hashtable pictureList = null;

	private MediaTracker tracker = null;

	private Component component = null;

	private File globallTar = null;

	/* containing list of requested tar or tar.bz2!*/
	private LinkedList tarList = null;

	private LinkedList temporaryFiles = null;
	
	private int iconSize=16;
	
	/* what to provide as result text/zip ... */
	private int resultType=DEFAULT_RESULT_TYPE;
	
	/* component is parent for dialogue/modal dialogue or new windows */
	private Component parentComponent=null;

	String tmppath = null;

	String tmpdir = null;

	String separator = null;
	
	String workingDir=null;

	private DataProvider() {
		pictureList = new Hashtable();
		tarList = new LinkedList();
		temporaryFiles = new LinkedList();
		tmppath = System.getProperty("java.io.tmpdir");
		tmpdir = String.valueOf((int) (1000000 * Math.random()));
		separator = System.getProperty("file.separator");
		workingDir=tmppath + separator + tmpdir;
	}

	/**
	 * create hiden DataProvider object
	 * 
	 */
	private static DataProvider init() {
		if (DataProvider.self == null) {
			DataProvider.self = new DataProvider();
			/* init temporary directory */
			File dir = new File(DataProvider.self.tmppath
					+ DataProvider.self.separator + DataProvider.self.tmpdir);
			if (!dir.mkdir())
				Debug.log("Cannot create directory: " + dir.getPath());

		}
		return DataProvider.self;
	}

	public static Image getImage(String name) {
		return getImage(name,init().iconSize);
	}
	/**
	 * Create Image, store it localy in hash map
	 * 
	 * @TODO: return instanty, and notify caller when image is ready
	 * @param name
	 * @return
	 */
	public static Image getImage(String name, int size) {
		DataProvider dp = init();
		if (dp.pictureList.containsKey(name)) {
			return dp.getImageFromList(name+size);
		}
		String path = imagesPath + "/" + name;
		int count = 0;
		InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
//		BufferedInputStream stream = new BufferedInputStream(is);
//		BufferedInputStream imgStream = new BufferedInputStream(dp.getClass()
//				.getResourceAsStream(path));
		BufferedInputStream imgStream = new BufferedInputStream(is);

		if (imgStream != null) {
			byte buf[] = new byte[DataProvider.MAX_IMAGE_SIZE];
			try {
				count = imgStream.read(buf);
				imgStream.close();
			} catch (java.io.IOException ioe) {
				Debug.log("DataProivder.getImage: Couldn't read "
						+ "stream from file: " + path+ "\n"+ioe.getMessage());
				ioe.printStackTrace();
				return null;
			}
			if (count <= 0) {
				Debug.log("DataProivder.getImage: Empty file: " + path);
				return null;		
			}
			Image reti = Toolkit.getDefaultToolkit().createImage(buf);
			if (dp.tracker != null) {
				System.out.println("using tracker in getImage");
				int trackID = dp.pictureList.size() + 1;
				dp.tracker.addImage(reti, trackID);
				try {
					dp.tracker.waitForID(trackID);
				} catch (InterruptedException e) {
					Debug.log("DataProvider.getImage: tracker interrupted");
				}
			}
			int width, height;
			if(reti.getHeight(null)>reti.getWidth(null)){
				height=size;
				width=(int)(reti.getWidth(null)*((float)size/reti.getHeight(null)));
			}
			else{
				width=size;
				height=(int)(reti.getHeight(null)*((float)size/reti.getWidth(null)));
			}
			Image toStore= reti.getScaledInstance(width, height, Image.SCALE_SMOOTH);
			dp.storeImage(name+size, toStore);
			Debug.log("DataProvider.getImage(): image created with path: " + path + " class: "
					+ reti);
			return toStore;
		} else {
			Debug.log("DataProivder.getImage: cannot create image with path "
					+ (path != null ? path : "null")+ " stream is null");
			return null;
		}
	}
	public static void setIconSize(int size){
		DataProvider dp = init();
		dp.iconSize=size;
		
	}

	/**
	 * needed for tracking down loading of images this will start waiting for
	 * all (not loaded) images!!!
	 * 
	 * @param c
	 */
	public static void setTrackerComponent(Component c) {
		DataProvider dp = init();
		dp.component = c;
		dp.tracker = new MediaTracker(c);
		int i = 0;
		for (Enumeration images = dp.pictureList.elements(); images
				.hasMoreElements();) {
			Image image = (Image) images.nextElement();
			dp.tracker.addImage(image, i++);
		}
		System.out.println("using tracker in setTracker for: " + i
				+ " components " + dp.pictureList.size());
		try {
			dp.tracker.waitForAll();
		} catch (InterruptedException e) {
			Debug.log("DataProvider.setTrackerComponent: tracker interupted");
		}
	}

	/**
	 * return filie where all logs can be stored. Create new file if needed.
	 * 
	 * @return
	 */
	public static synchronized File getGlobalTarName() {
		DataProvider dp = init();
		if (dp.globallTar == null) {
			dp.globallTar = getTarName("log");
		}
		return dp.globallTar;
	}

	/**
	 * get Tar name (in temporary directory)
	 * 
	 * @param name -
	 *            name of tar
	 * @return - return file in local temporary directory
	 */
	public static synchronized File getTarName(String name) {
		DataProvider dp = init();
		File reti = null;
		reti = new File(dp.workingDir + dp.separator
				+ name + ".tar");
		dp.tarList.add(reti);
		return reti;
	}

	public static synchronized File getTemporaryFile(String name) {
		DataProvider dp = init();
		File reti = null;
		reti = new File(dp.workingDir + dp.separator
				+ name + String.valueOf((int) (100 * Math.random())));
		dp.temporaryFiles.add(reti);
		return reti;
	}
	
	public static void showMessage(String title, String message) {
		DataProvider dp = init();
		File reti = null;
		JOptionPane.showMessageDialog(dp.parentComponent, message, title,JOptionPane.INFORMATION_MESSAGE);	
	}

//	/**
//	 * 
//	 * @param tarFile
//	 * @param logs
//	 * @return
//	 */
//	public static File packTar(File tarFile,Collection logs){
//		File reti = new File(tarFile.getAbsolutePath()+".bz2");
//		
//		return reti;
//	}
	/**
	 * synchronized method do add files to provided tar. Synchornized will garant only one process will access
	 * tar.
	 * @param tarFile
	 * @param logs
	 */
	public static synchronized void addFilesToTar(File tarFile,Collection logs){
		DataProvider dp = init();
		Runtime runtime = Runtime.getRuntime();
		Process p=null;
		StringBuffer cmd = new StringBuffer();
		
		if(tarFile.exists())
			cmd.append("tar -C "+(dp.workingDir)+" -rf " + tarFile);
		else
			cmd.append("tar -C "+(dp.workingDir)+" -cf " + tarFile);
		
		for (Iterator iter = logs.iterator(); iter.hasNext();)
			cmd.append(" " + ((String)iter.next()).replaceAll(dp.workingDir, "."));
		try {
			Debug.log("DataProvider.addFilesToTar: running command: "+cmd.toString());
			p = runtime.exec(cmd.toString());
			p.waitFor();
			Debug.log("DataProvider.addFilesToTar(): finished ("+p.exitValue()+")");
		} catch (IOException e) {
			Debug
					.log("DataProvider.addFilesToTar(): IOException during tarring of files");
		} catch (InterruptedException e) {
			Debug
					.log("DataProvider.addFilesToTar(): interrupted during tarring of files");
		} finally {
			if (p != null) {
				p.destroy();
				p = null;
			}
		}
	}
	
	/**
	 *  set result type for output of executed commands 
	 **/
	public static void setType(int type){
		init().resultType=type;
	}
	
	/**
	 *  
	 * @return - int representing type of result, see LogModule.TYPE*
	 */
	public static int getType(){
		return init().resultType;
	}
	/**
	 * get size of icon 
	 * @return - int size of icon
	 */
	public static int getIconSize() {
		return init().iconSize;
	}

	/**
	 * set parrent component, for dialogs and new windows
	 * @param c
	 */
	public static void setParrentComponent(Component c){
		init().parentComponent=c;
	}
	
	/**
	 * This method will pack each tar (one by one) files to tar.bz2
	 * and return as array 
	 */
	public static synchronized LinkedList packTars(){
		DataProvider dp = init();
		Runtime runtime = Runtime.getRuntime();
		Process p=null;
		StringBuffer cmd = new StringBuffer();
		
		LinkedList tarBZList=new LinkedList(dp.tarList);
		dp.tarList.clear();
		for (Iterator iterator = tarBZList.iterator(); iterator.hasNext();) {
			File tar = (File) iterator.next();
			if(tar.exists() && tar.getName().endsWith(".tar")){
				//pack it and return under different name
				File tarbz = new File (tar.getAbsolutePath()+".bz2");
				cmd.append("bzip2 -z "+tar.getAbsolutePath());
				try {
					Debug.log("DataProvider.packTars: running command: "+cmd.toString());
					p = runtime.exec(cmd.toString());
					p.waitFor();
					Debug.log("DataProvider.packTars(): finished ("+p.exitValue()+")");
				} catch (IOException e) {
					Debug
							.log("DataProvider.packTars(): IOException during tarring of files");
				} catch (InterruptedException e) {
					Debug
							.log("DataProvider.packTars(): interrupted during tarring of files");
				} finally {
					if (p != null) {
						p.destroy();
						p = null;
					}
					dp.tarList.add(tarbz);
				}
			}
			else{
				//return it back untouched
				dp.tarList.add(tar);
			}
		}
		return dp.tarList;
	}
	
	
	/* private stuff */
	/**
	 * just proxy for Hashtable
	 */
	private Image getImageFromList(String name) {
		return (Image) pictureList.get(name);
	}

	private void storeImage(String name, Image image) {
		pictureList.put(name, image);
	}
	
}
